<template>
  <el-card class="movie-wrapper" v-loading="loading">
    <el-form
      :model="movie"
      ref="movie"
      label-position="right"
      label-width="120px"
      size="small"
      class="form"
      :rules="rules"
    >
      <el-form-item prop="title" label="电影名称">
        <el-input type="text" v-model="movie.title" clearable />
      </el-form-item>
      <el-form-item prop="origin_title" label="电影原名">
        <el-input type="text" v-model="movie.origin_title" :placeholder="multipleTip" clearable />
      </el-form-item>
      <el-form-item prop="cover" label="电影封面">
        <image-upload @done="handleCoverUpload" />
        <image-preview :src="movie.cover" @remove="handleCoverRemove" style="margin-top: 10px;" />
      </el-form-item>
      <el-form-item prop="genres" label="电影类型">
        <el-input type="text" v-model="movie.genres" :placeholder="multipleTip" clearable />
      </el-form-item>
      <el-form-item prop="pub_year" label="上映年份">
        <el-date-picker type="year" v-model="movie.pub_year" clearable />
      </el-form-item>
      <el-form-item prop="release_date" label="上映日期">
        <el-date-picker type="date" v-model="movie.release_date" />
      </el-form-item>
      <el-form-item prop="duration" label="电影时长">
        <el-input type="text" v-model="movie.duration">
          <template slot="suffix">分钟</template>
        </el-input>
      </el-form-item>
      <el-form-item prop="lang" label="制片语言">
        <el-input type="text" v-model="movie.lang" :placeholder="multipleTip" />
      </el-form-item>
      <el-form-item prop="regions" label="制片国家/地区">
        <el-input type="text" v-model="movie.regions" :placeholder="multipleTip" />
      </el-form-item>
      <el-form-item prop="directors" label="导演">
        <el-input type="text" v-model="movie.directors" :placeholder="multipleTip" />
      </el-form-item>
      <el-form-item prop="writers" label="编剧">
        <el-input type="text" v-model="movie.writers" :placeholder="multipleTip" />
      </el-form-item>
      <el-form-item prop="actors" label="演员">
        <el-input type="text" v-model="movie.actors" :placeholder="multipleTip" />
      </el-form-item>
      <el-form-item prop="douban_rating" label="豆瓣评分">
        <el-input type="text" v-model="movie.douban_rating" />
      </el-form-item>
      <el-form-item prop="douban_rating_count" label="豆瓣评分人数">
        <el-input type="text" v-model="movie.douban_rating_count" />
      </el-form-item>
      <el-form-item prop="douban_summary" label="豆瓣简介">
        <el-input
          type="textarea"
          v-model="movie.douban_summary"
          :autosize="{ minRows: 2, maxRows: 4}"
        />
      </el-form-item>
      <el-form-item prop="imdb_rating" label="IMDb评分">
        <el-input type="text" v-model="movie.imdb_rating" />
      </el-form-item>
      <el-form-item prop="imdb_rating_count" label="IMDb评分人数">
        <el-input type="text" v-model="movie.imdb_rating_count" />
      </el-form-item>
      <el-form-item prop="imdb_summary" label="IMDb简介">
        <el-input
          type="textarea"
          v-model="movie.imdb_summary"
          :autosize="{ minRows: 2, maxRows: 4}"
        />
      </el-form-item>
      <el-form-item prop="alias" label="别名">
        <el-input type="text" v-model="movie.alias" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="handleSubmit" style="width: 120px">提交</el-button>
      </el-form-item>
    </el-form>
  </el-card>
</template>

<script>
import { getMovie, updateMovie, createMovie } from "@/services";
import { filterEmptyProp } from "@/utils/util";
const STATUS = {
  UPDATE: "UPDATE",
  CREATE: "CREATE"
};
const DEFAULT_MOVIE_DATA = {
  title: "",
  cover: "",
  id: "",
  origin_title: "",
  pub_year: "",
  release_date: "",
  douban_id: "",
  douban_summary: "",
  imdb_id: "",
  imdb_summary: "",
  imdb_rating_count: "",
  duration: "",
  douban_rating: "",
  douban_rating_count: "",
  imdb_rating: "",
  lang: "",
  regions: "",
  actors: "",
  directors: "",
  writers: "",
  alias: "",
  genres: ""
};

export default {
  name: "Movie",
  data() {
    const requireChecker = ({
      message = "该项必须填写",
      trigger = "blur"
    } = {}) => ({
      required: true,
      message,
      trigger
    });

    const isEmpty = value => value === "";

    const lenChecker = (len, trigger = "blur") => {
      return { min: 0, max: len, message: `最多输入${len}个字符`, trigger };
    };

    const decimalChecker = (len, trigger = "blur") => {
      const validator = (rule, value, callback) => {
        if (isEmpty(value)) {
          return callback();
        }
        const parsed = String(value);
        const dotPos = parsed.indexOf(".");
        if (dotPos < 0) {
          return callback();
        }
        const decimalLength = parsed.length - 1 - dotPos;
        if (decimalLength > len) {
          return callback(new Error(`最多可输入${len}位小数`));
        }
        callback();
      };
      return { validator, trigger };
    };

    const numberChecker = (trigger = "blur") => {
      const validator = (rule, value, callback) => {
        if (isEmpty(value)) {
          return callback();
        }
        const isNumber = !isNaN(Number(value));
        if (!isNumber) {
          return callback(new Error("请输入数字"));
        }
        callback();
      };
      return { validator, trigger };
    };

    const rangeChecker = (min, max, trigger = "blur") => {
      const validator = (rule, value, callback) => {
        if (isEmpty(value)) {
          return callback();
        }
        const parsed = Number(value);
        if (max < parsed || min > parsed) {
          return callback(new Error(`请输入${min}-${max}之间的数字`));
        }
        callback();
      };
      return {
        validator,
        trigger
      };
    };

    const intChecker = (trigger = "blur") => {
      const validator = (rule, value, callback) => {
        if (isEmpty(value)) {
          return callback();
        }
        const isInt = /^-?[1-9]\d*$|0/.test(value);
        if (!isInt) {
          return callback(new Error("请输入整数"));
        }
        callback();
      };
      return { validator, trigger };
    };

    const setTrigger = (trigger = "blur") => (checkers = []) =>
      checkers.map(checker => ({ ...checker, trigger }));

    const setChangeTrigger = setTrigger("change");

    return {
      movie: { ...DEFAULT_MOVIE_DATA },
      multipleTip: "多个值请用英文逗号分隔",
      loading: false,
      rules: {
        title: [requireChecker(), lenChecker(128)],
        origin_title: [lenChecker(128)],
        douban_rating: [
          numberChecker(),
          rangeChecker(0, 10),
          decimalChecker(1)
        ],
        douban_rating_count: [numberChecker(), intChecker()],
        imdb_rating: [numberChecker(), rangeChecker(0, 10), decimalChecker(1)],
        imdb_rating_count: [numberChecker(), intChecker()],
        duration: [requireChecker(), numberChecker(), intChecker()],
        directors: [requireChecker()],
        pub_year: [requireChecker({ trigger: 'change' })],
        actors: [requireChecker()],
        douban_summary: [requireChecker()],
        regions: [requireChecker()],
        genres: [requireChecker()],
        lang: [lenChecker(32)],
        douban_id: [lenChecker(32)],
        imdb_id: [lenChecker(32)],
        alias: [lenChecker(256)],
        cover: [
          requireChecker({ trigger: "change" }),
          lenChecker(1024, "change")
        ]
      }
    };
  },
  watch: {
    $route: {
      immediate: true,
      handler(v) {
        this.movie.id = v.params.id;
      }
    },
    "movie.id": {
      immediate: true,
      handler(v) {
        if (v) {
          this.handleMovieFetch();
        } else {
          this.clear();
        }
      }
    }
  },
  methods: {
    handleMovieFetch() {
      this.loading = true;
      getMovie(this.movie.id)
        .then(res => {
          this.movie = this.handleBeforeReceive(res);
        })
        .catch(err => {
          this.$message.error(`电影数据获取失败: ${err.message}`);
        })
        .finally(() => {
          this.loading = false;
        });
    },
    clear() {
      this.$refs.movie && this.$refs.movie.resetFields();
      this.movie = { ...DEFAULT_MOVIE_DATA };
    },
    async handleSubmit() {
      if (!(await this.validate())) {
        return;
      }

      const func = this.isUpdate ? updateMovie : createMovie;
      this.loading = true;
      func(this.handleBeforeSubmit(this.movie))
        .then(data => {
          const message = this.isUpdate ? `更新成功` : `创建成功`;
          this.$message.success(message);
          if (this.isUpdate) {
            this.movie = this.handleBeforeReceive(data);
          } else {
            this.$router.push(`/?keyword=${this.movie.title}`);
          }
        })
        .catch(err => this.$message.error(err.message))
        .finally(() => (this.loading = false));
    },
    handleCoverUpload(src) {
      this.movie.cover = src;
    },
    handleCoverRemove() {
      this.movie.cover = "";
    },
    validate() {
      return new Promise(async resolve => {
        try {
          await this.$refs.movie.validate();
          resolve(true);
        } catch (e) {
          resolve(false);
        }
      });
    },
    handleBeforeSubmit(movie) {
      const origin = { ...movie };
      const newMovie = filterEmptyProp({
        ...origin,
        genres: origin.genres && this.handleStr2Array(origin.genres),
        pub_year: origin.pub_year && new Date(origin.pub_year).getFullYear(),
        release_date:
          origin.release_date && new Date(origin.release_date).toJSON(),
        regions: origin.regions && this.handleStr2Array(origin.regions),
        actors: origin.actors && this.handleStr2Array(origin.actors),
        directors: origin.directors && this.handleStr2Array(origin.directors),
        writers: origin.writers && this.handleStr2Array(origin.writers),
        imdb_rating_count:
          origin.imdb_rating_count && +origin.imdb_rating_count,
        duration: origin.duration && +origin.duration,
        douban_rating: origin.douban_rating && +origin.douban_rating,
        douban_rating_count:
          origin.douban_rating_count && +origin.douban_rating_count,
        imdb_rating: origin.imdb_rating && +origin.imdb_rating
      });

      return newMovie;
    },
    handleBeforeReceive(movie) {
      const origin = {
        ...movie
      };
      const newMovie = {
        ...origin,
        genres: this.handleArray2Str(origin.genres),
        pub_year: (origin.pub_year && new Date(origin.pub_year + "")) || null,
        release_date:
          (origin.release_date && new Date(origin.release_date)) || null,
        regions: this.handleArray2Str(origin.regions),
        actors: this.handleArray2Str(origin.actors),
        directors: this.handleArray2Str(origin.directors),
        writers: this.handleArray2Str(origin.writers)
      };
      return newMovie;
    },
    handleArray2Str(arr) {
      arr = arr || [];
      return arr.map(({ name }) => name).join(",");
    },
    handleStr2Array(str = "") {
      return str
        .split(",")
        .map(s => s.trim())
        .filter(v => v);
    }
  },
  computed: {
    isUpdate() {
      return !!this.movie.id;
    }
  }
};
</script>

<style lang="scss" scoped>
.movie-wrapper {
  min-height: calc(100vh - 56px - 48px);
  padding: 20px;
  .form {
    width: 480px;
    margin: 0 auto;
  }
}
</style>